/*
 * File Name: P2PReceiver.java
 */
package co.edu.uniandes.intercon.server.networking;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

import co.edu.uniandes.intercon.core.model.ApplicationDto;
import co.edu.uniandes.intercon.core.model.BaseDto;
import co.edu.uniandes.intercon.core.model.EMessageType;
import co.edu.uniandes.intercon.core.model.P2PMessage;
import co.edu.uniandes.intercon.core.model.RemoteApplicationList;
import co.edu.uniandes.intercon.server.core.Constants;
import co.edu.uniandes.intercon.server.core.P2PException;
import co.edu.uniandes.intercon.server.util.ConnectionHelper;
import co.edu.uniandes.intercon.server.util.P2PLogger;

/**
 * Class created to work as a <code>UDP</code> message listener
 * 
 * @author Fabian Gutierrez [fagossa@gmail.com]
 */
public class UDPReceiver {

	private static MulticastSocket socket;
	private static InetAddress group;
	private static UDPReceiver.ReceiverThread thread;
	private static boolean continueThread = true;

	/**
	 * Start the receiving process
	 * 
	 * @throws P2PException
	 *             When the communication could not be startted
	 */
	public static void start() throws P2PException {
		continueThread = true;
		try {
			if (group == null) {
				group = InetAddress.getByName(Constants.MCAST_ADDR);
			}

			socket = new MulticastSocket(Constants.DEST_PORT);
			socket.joinGroup(group);

			thread = new ReceiverThread();
			thread.start();
		} catch (Exception e) {
			P2PLogger.logError("Error al enviar mensaje", e);
			throw new P2PException("Error comunicandose con el sistema remoto");
		} finally {

		}
	}

	/**
	 * Release the current open <code>UDP</code> channel
	 */
	public static void release() {
		try {
			continueThread = false;
			if (socket.isConnected()) {
				socket.leaveGroup(group);
			}
		} catch (IOException e) {
			P2PLogger.logError("Error dejando el grupo UDP", e);
		} finally {
			ConnectionHelper.close(socket);
		}
	}

	/**
	 * Inner class used to control the message receiving process
	 * 
	 * @author Fabian Gutierrez [fagossa@gmail.com]
	 */
	static class ReceiverThread extends Thread {

		/**
		 * @see Thread#run()
		 */
		public void run() {
			while (continueThread) {
				ObjectInputStream stream = null;
				ByteArrayInputStream bytes = null;
				try {
					byte buf[] = new byte[65508];
					DatagramPacket packet = new DatagramPacket(buf, buf.length);
					if (socket != null) {
						try {
							if (true) { //socket.isConnected()
								socket.receive(packet);

								System.out
										.println("UDPReceiver.paquete recibido: "
												+ packet.getData());
								bytes = new ByteArrayInputStream(packet
										.getData());
								stream = new ObjectInputStream(bytes);

								processIncomingMessage((P2PMessage) stream
										.readObject());
							}

						} catch (java.net.SocketException ex) {
							// ignored by default
						}
					}
				} catch (Exception e) {
					P2PLogger.logError("Error al recibir mensaje", e);
				} finally {
					ConnectionHelper.close(bytes);
					ConnectionHelper.close(stream);
				}
			}
		}
	}

	/*
	 * Process the message received
	 */
	private static void processIncomingMessage(P2PMessage p2pMessage) {
		BaseDto applicationDto = p2pMessage.getApplicationDto();

		if (p2pMessage.getType() == EMessageType.ADD) {
			if (!RemoteApplicationList.getInstance().exist(
					(ApplicationDto) applicationDto)) {
				RemoteApplicationList.getInstance().addMessage(applicationDto);
			}
		} else if (p2pMessage.getType() == EMessageType.DELETE) {
			RemoteApplicationList.getInstance().remove(
					(ApplicationDto) applicationDto);
		}
	}

}
